package eu.catengine.graphic;

import java.util.HashMap;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.opengl.GLUtils;
import eu.catengine.common.iface.IDisposable;
import eu.catengine.primitive.Point;
import eu.catengine.primitive.Size;

/**
 * Klasa przechowująca dane dotyczące czcionek.
 * 
 * @author mbienkowski
 */
public class Font extends Texture implements IDisposable
{
    private String mFontName;
    private Typeface mTypeFace;
    private float mFontSize;
    private Paint mPaint;

    private float mCharWidthMax;
    private HashMap<Character, Float> mCharWidth;
    private float mCharHeight;

    private int mPaddingX = 2;
    private int mPaddingY = 2;

    private int mCellWidth;
    private int mCellHeight;

    private int mItemsInRow;
    private int mItemsInCol;

    public Font(GL10 gl, String fontName, Typeface typeFace, float fontSize, String dataset)
    {
	mFontName = fontName;
	mTypeFace = typeFace;
	mFontSize = fontSize;

	// Ustalanie pedzla do rysowania
	mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
	mPaint.setAntiAlias(true);
	mPaint.setTypeface(mTypeFace);
	mPaint.setTextSize(mFontSize);
	mPaint.setARGB(0xff, 0xff, 0xff, 0xff);

	Paint.FontMetrics fm = mPaint.getFontMetrics();
	mCharHeight = (float) Math.ceil(Math.abs(fm.bottom) + Math.abs(fm.top));

	// Zapisywanie wielkości znaków
	// mCharWidth = new float[dataset.length()];
	mCharWidth = new HashMap<Character, Float>();
	mCharWidthMax = 0;

	char[] s = new char[2];
	float[] w = new float[2];

	for (int i = 0; i < dataset.length(); i++)
	{
	    char c = dataset.charAt(i);
	    s[0] = c;
	    mPaint.getTextWidths(s, 0, 1, w);

	    mCharWidth.put(c, w[0]);

	    if (w[0] > mCharWidthMax)
	    {
		mCharWidthMax = w[0];
	    }
	}

	// Określenie wielkości komórek oraz tekstury
	mCellWidth = (int) mCharWidthMax + mPaddingX;
	mCellHeight = (int) mCharHeight + mPaddingY;

	int field = dataset.length() * mCellWidth * mCellHeight;
	int tmpWidth = nextPowerOfTwo((int) Math.ceil(Math.sqrt(field)));
	mTextureSize = new Size(tmpWidth, tmpWidth);

	mItemsInRow = tmpWidth / mCellWidth;
	mItemsInCol = tmpWidth / mCellHeight;

	if ((mItemsInCol * mItemsInRow) < dataset.length())
	{
	    mTextureSize.width *= 2;
	    mItemsInRow *= 2;
	}

	// Przypisanie danych odnośnie rozmiaru obrazka
	mImageSize = mTextureSize;

	// Tworzenie bitmapy
	Bitmap bitmap = Bitmap.createBitmap((int) mTextureSize.width, (int) mTextureSize.height, Bitmap.Config.ARGB_4444);
	Canvas canvas = new Canvas(bitmap);
	bitmap.eraseColor(0);

	// Przerysowanie wszystkich znaków na bitmape
	for (int i = 0; i < dataset.length(); i++)
	{
	    int x = (int) ((i % mItemsInRow) * mCellWidth);
	    int y = (int) ((i / mItemsInRow) * mCellHeight) + mCellHeight;

	    canvas.drawText(dataset.substring(i, i + 1), x, y, mPaint);
	}

	mTextureRegionHashMap = new HashMap<String, TextureRegion>();

	float mRegionCoords[] = new float[] { 0.0f, 1.0f, // top left (V2)
		0.0f, 0.0f, // bottom left (V1)
		1.0f, 1.0f, // top right (V4)
		1.0f, 0.0f // bottom right (V3)
	};

	// Generate one texture pointer and bind it to our array
	gl.glGenTextures(1, mTexture, 0);
	gl.glBindTexture(GL10.GL_TEXTURE_2D, mTexture[0]);

	// Create nearest filtered texture
	gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
	gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

	// Use Android GLUtils to specify a two-dimensional texture image from
	// our bitmap
	GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

	// Clean up
	bitmap.recycle();

	// Adding regions to hash map
	mTextureRegionHashMap.put("default", new TextureRegion(mRegionCoords));

	for (int i = 0; i < dataset.length(); i++)
	{
	    int x = (int) ((i % mItemsInRow) * mCellWidth);
	    int y = (int) ((i / mItemsInRow) * mCellHeight + fontSize / 2);

	    this.addTextureRegion(dataset.substring(i, i + 1), new Point(x, y), new Size(mCharWidthMax, mCharHeight));
	}
    }

    /**
     * Zwraca nazwę czcionki.
     */
    public String getFontName()
    {
	return mFontName;
    }

    /**
     * Zwraca rozmiar czcionki.
     */
    public float getFontSize()
    {
	return mFontSize;
    }

    /**
     * Zwraca szerokość komórki w której przechowywana jest pojedyczna litera na
     * teksturze.
     */
    public int getCellWidth()
    {
	return mCellWidth;
    }

    /**
     * Zwraca wysokość komórki w której przechowywana jest pojedyczna litera na
     * teksturze.
     */
    public int getCellHeight()
    {
	return mCellHeight;
    }

    /**
     * Zwraca szerokość znaku.
     * 
     * @param c
     *            <code>char</code> Podany znak.
     * @return <code>float</code> Szerokość znaku c.
     */
    public float getCharWidth(char c)
    {
	return mCharWidth.get(c);
    }

    /**
     * Zwraca wysokośc znaku.<br>
     * Domyślnie przyjęte jako wysokość czcionki.
     */
    public float getCharHeight()
    {
	return mCharHeight;
    }

    /**
     * Zwraca szerokość tekstu.
     * 
     * @param text
     *            <code>String</code> Podany tekst.
     * @return <code>float</code> Szerokość tekstu text.
     */
    public float getTextWidth(String text)
    {
	float width = 0;

	for (int i = 0; i < text.length(); i++)
	{
	    if (text.charAt(i) == '\n')
	    {
		continue;
	    }
	    else if (text.charAt(i) == '\t')
	    {
		continue;
	    }

	    width += getCharWidth(text.charAt(i));
	}

	return width;
    }

    @Override
    public void dispose()
    {
	mCharWidth.clear();

	mFontName = null;
	mTypeFace = null;
	mPaint = null;
	mCharWidth = null;

	super.dispose();
    }
}
